//
//  NestedOutterScrollViews.swift
//  
//
//  Created by bo wang on 2020/9/29.
//  Copyright © 2020 wangbo. All rights reserved.
//

import UIKit

/// 共享的 NestedScrollContext 实例提供者
/// 外部ScrollView和内部ScrollView需要使用同一个上下文才可以实现嵌套滚动
@objc protocol NestedScrollContextProvider: NSObjectProtocol {
    var scrollingCtx: NestedScrollContext? { get }
}

/// 用于外部ScrollView和内部ScrollView同步contentOffset的上下文对象
/// 通过设置 maxOffsetY 让外部的ScrollView滚动到一定offset时停止，开始内部的ScrollView滚动。
class NestedScrollContext: NSObject {
    /// 滚动内部时，外部scrollView停留的位置
    var maxOffsetY: CGFloat
    fileprivate var outerOffset: CGPoint = CGPoint.zero
    fileprivate var innerOffset: CGPoint = CGPoint.zero
    init(maxOffsetY: CGFloat) {
        self.maxOffsetY = maxOffsetY
    }
}

/// 嵌套的滚动结构中，在外部滚动的UIScrollView
class NestedOutterScrollView: UIScrollView, UIGestureRecognizerDelegate {
    /// 设置代理，从代理处获得共享的 NestedScrollContext，可以通过IB连线设置。
    @IBOutlet weak var scrollingCtxProvider: NestedScrollContextProvider?
    override var contentOffset: CGPoint {
        didSet {
            guard let scrollingCtx = scrollingCtxProvider?.scrollingCtx else { return }
            if contentOffset.y != oldValue.y {
                // 下层scrollView滑动
                if contentOffset.y >= scrollingCtx.maxOffsetY || scrollingCtx.innerOffset.y > 0 {
                    // 上层的scrollView滑动，则下层的scrollView保持最大滑动距离
                    contentOffset.y = scrollingCtx.maxOffsetY
                    showsVerticalScrollIndicator = false
                    scrollsToTop = false
                } else {
                    // 否则，上层不动，下层滑动
                    scrollsToTop = true
                    showsVerticalScrollIndicator = true
                }
                // 同步offset到上下文
                scrollingCtx.outerOffset = contentOffset
            }
        }
    }
    
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        if otherGestureRecognizer is UIScreenEdgePanGestureRecognizer {
            return false
        }
        return true
    }
}

/// 嵌套的滚动结构中，在内部滚动的UIScrollView
class NestedInnerScrollView: UIScrollView {
    /// 设置代理，从代理处获得共享的 NestedScrollContext，可以通过IB连线设置。
    @IBOutlet weak var scrollingCtxProvider: NestedScrollContextProvider?
    override var contentOffset: CGPoint {
        didSet {
            guard let scrollingCtx = scrollingCtxProvider?.scrollingCtx else { return }
            if contentOffset.y != oldValue.y {
                // 上层滑动
                if scrollingCtx.outerOffset.y == 0 {
                    // 下层已经滑到顶部了
                } else if Int(scrollingCtx.outerOffset.y * 10) < Int(scrollingCtx.maxOffsetY * 10) {
                    // 下层的offset < 下层可滑动最大值，说明下层还需要滑动，上层不动offset为0
                    contentOffset.y = 0
                    showsVerticalScrollIndicator = false
                } else {
                    showsVerticalScrollIndicator = true
                }
                // 不管怎么样，滑动即同步offset到上下文
                scrollingCtx.innerOffset = contentOffset
            }
        }
    }
}

/// 嵌套的滚动结构中，在内部滚动的UITableView
class NestedInnerTableView: UITableView {
    /// 设置代理，从代理处获得共享的 NestedScrollContext，可以通过IB连线设置。
    @IBOutlet weak var scrollingCtxProvider: NestedScrollContextProvider?
    override var contentOffset: CGPoint {
        didSet {
            guard let scrollingCtx = scrollingCtxProvider?.scrollingCtx else { return }
            if contentOffset.y != oldValue.y {
                // 上层滑动
                if scrollingCtx.outerOffset.y == 0 {
                    // 下层已经滑到顶部了
                } else if Int(scrollingCtx.outerOffset.y * 10) < Int(scrollingCtx.maxOffsetY * 10) {
                    // 下层的offset < 下层可滑动最大值，说明下层还需要滑动，上层不动offset为0
                    contentOffset.y = 0
                    showsVerticalScrollIndicator = false
                } else {
                    showsVerticalScrollIndicator = true
                }
                // 不管怎么样，滑动即同步offset到上下文
                scrollingCtx.innerOffset = contentOffset
            }
        }
    }
}

/// 嵌套的滚动结构中，在内部滚动的UICollectionView
class NestedInnerCollectionView: UICollectionView {
    /// 设置代理，从代理处获得共享的 NestedScrollContext，可以通过IB连线设置。
    @IBOutlet weak var scrollingCtxProvider: NestedScrollContextProvider?
    override var contentOffset: CGPoint {
        didSet {
            guard let scrollingCtx = scrollingCtxProvider?.scrollingCtx else { return }
            if contentOffset.y != oldValue.y {
                // 上层滑动
                if scrollingCtx.outerOffset.y == 0 {
                    // 下层已经滑到顶部了
                } else if Int(scrollingCtx.outerOffset.y * 10) < Int(scrollingCtx.maxOffsetY * 10) {
                    // 下层的offset < 下层可滑动最大值，说明下层还需要滑动，上层不动offset为0
                    contentOffset.y = 0
                    showsVerticalScrollIndicator = false
                } else {
                    showsVerticalScrollIndicator = true
                }
                // 不管怎么样，滑动即同步offset到上下文
                scrollingCtx.innerOffset = contentOffset
            }
        }
    }
}
